home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / turbopas / tutorpas.arc / TUTOR1.DOC < prev    next >
Encoding:
Text File  |  1988-07-30  |  16.6 KB  |  462 lines

  1. O
  2. PA A
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.                             LET'S BUILD A COMPILER!
  30.  
  31.                                        By
  32.  
  33.                             Jack W. Crenshaw, Ph.D.
  34.  
  35.                                   24 July 1988
  36.  
  37.  
  38.                               Part I: INTRODUCTION
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69. PA A
  70.  
  71.  
  72.  
  73.  
  74.  
  75.        *****************************************************************
  76.        *                                                               *
  77.        *                        COPYRIGHT NOTICE                       *
  78.        *                                                               *
  79.        *   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
  80.        *                                                               *
  81.        *****************************************************************
  82.  
  83.  
  84.        INTRODUCTION
  85.  
  86.  
  87.        This series of articles is a tutorial on the theory  and practice
  88.        of  developing language parsers and compilers.    Before  we  are
  89.        finished,  we  will  have  covered  every   aspect   of  compiler
  90.        construction, designed a new programming  language,  and  built a
  91.        working compiler.
  92.  
  93.        Though I am not a computer scientist by education (my Ph.D. is in
  94.        a different  field, Physics), I have been interested in compilers
  95.        for many years.  I have  bought  and tried to digest the contents
  96.        of virtually every  book  on  the  subject ever written.  I don't
  97.        mind  telling you that it was slow going.    Compiler  texts  are
  98.        written for Computer  Science  majors, and are tough sledding for
  99.        the rest of us.  But over the years a bit of it began to seep in.
  100.        What really caused it to jell was when I began  to  branch off on
  101.        my own and begin to try things on my own computer.  Now I plan to
  102.        share with you what I have  learned.    At the end of this series
  103.        you will by no means be  a  computer scientist, nor will you know
  104.        all the esoterics of  compiler  theory.    I intend to completely
  105.        ignore the more theoretical  aspects  of  the  subject.  What you
  106.        _WILL_ know is all  the  practical aspects that one needs to know
  107.        to build a working system.
  108.  
  109.        This is a "learn-by-doing" series.  In the course of the series I
  110.        will be performing  experiments  on  a  computer.    You  will be
  111.        expected to follow along,  repeating  the  experiments that I do,
  112.        and  performing  some  on your own.  I will be using Turbo Pascal
  113.        4.0 on a PC  clone.   I will periodically insert examples written
  114.        in TP.  These will be executable code, which you will be expected
  115.        to copy into your own computer and run.  If you don't have a copy
  116.        of  Turbo,  you  will be severely limited in how well you will be
  117.        able to follow what's going on.  If you don't have a copy, I urge
  118.        you to get one.  After  all,  it's an excellent product, good for
  119.        many other uses!
  120.  
  121.        Some articles on compilers show you examples, or show you  (as in
  122.        the case of Small-C) a finished product, which you can  then copy
  123.        and  use without a whole lot of understanding of how it works.  I
  124.        hope to do much more  than  that.    I  hope to teach you HOW the
  125.        things get done,  so that you can go off on your own and not only
  126.        reproduce what I have done, but improve on it.ABAB
  127.                                      - 2 -A*A*
  128.  
  129. PA A
  130.  
  131.  
  132.  
  133.  
  134.  
  135.        This is admittedly an ambitious undertaking, and it won't be done
  136.        in  one page.  I expect to do it in the course  of  a  number  of
  137.        articles.    Each  article will cover a single aspect of compiler
  138.        theory,  and  will  pretty  much  stand  alone.   If  all  you're
  139.        interested in at a given time is one  aspect,  then  you  need to
  140.        look only at that one article.  Each article will be  uploaded as
  141.        it  is complete, so you will have to wait for the last one before
  142.        you can consider yourself finished.  Please be patient.
  143.  
  144.        The average text on  compiler  theory covers a lot of ground that
  145.        we won't be covering here.  The typical sequence is:
  146.  
  147.         o An introductory chapter describing what a compiler is.
  148.  
  149.         o A chapter or two on syntax equations, using Backus-Naur Form
  150.           (BNF).
  151.  
  152.         o A chapter or two on lexical scanning, with emphasis on
  153.           deterministic and non-deterministic finite automata.
  154.  
  155.         o Several chapters on parsing theory, beginning with top-down
  156.           recursive descent, and ending with LALR parsers.
  157.  
  158.         o A chapter on intermediate languages, with emphasis on P-code
  159.           and similar reverse polish representations.
  160.  
  161.         o Many chapters on alternative ways to handle subroutines and
  162.           parameter passing, type declarations, and such.
  163.  
  164.         o A chapter toward the end on code generation, usually for some
  165.           imaginary CPU with a simple instruction set.  Most readers
  166.           (and in fact, most college classes) never make it this far.
  167.  
  168.         o A final chapter or two on optimization. This chapter often
  169.           goes unread, too.
  170.  
  171.  
  172.        I'll  be taking a much different approach in  this  series.    To
  173.        begin  with,  I  won't dwell long on options.  I'll be giving you
  174.        _A_ way that works.  If you want  to  explore  options,  well and
  175.        good ...  I  encourage  you  to do so ... but I'll be sticking to
  176.        what I know.   I also will skip over most of the theory that puts
  177.        people  to  sleep.  Don't get me  wrong:  I  don't  belittle  the
  178.        theory, and it's vitally important  when it comes to dealing with
  179.        the more tricky  parts  of  a  given  language.  But I believe in
  180.        putting first things first.    Here we'll be dealing with the 95%
  181.        of compiler techniques that don't need a lot of theory to handle.
  182.  
  183.        I  also  will  discuss only one approach  to  parsing:  top-down,
  184.        recursive descent parsing, which is the  _ONLY_  technique that's
  185.        at  all   amenable  to  hand-crafting  a  compiler.    The  other
  186.        approaches are only useful if you have a tool like YACC, and also
  187.        don't care how much memory space the final product uses.A6A6
  188.                                      - 3 -A*A*
  189.  
  190. PA A
  191.  
  192.  
  193.  
  194.  
  195.  
  196.        I  also take a page from the work of Ron Cain, the author of  the
  197.        original Small C.  Whereas almost all other compiler authors have
  198.        historically  used  an  intermediate  language  like  P-code  and
  199.        divided  the  compiler  into two parts (a front end that produces
  200.        P-code,  and   a  back  end  that  processes  P-code  to  produce
  201.        executable   object  code),  Ron  showed  us   that   it   is   a
  202.        straightforward  matter  to  make  a  compiler  directly  produce
  203.        executable  object  code,  in  the  form  of  assembler  language
  204.        statements.  The code will _NOT_ be the world's tightest code ...
  205.        producing optimized code is  a  much  more  difficult job. But it
  206.        will work, and work reasonably well.  Just so that I  don't leave
  207.        you with the impression that our end product will be worthless, I
  208.        _DO_ intend to show you how  to  "soup up" the compiler with some
  209.        optimization.
  210.  
  211.        Finally, I'll be  using  some  tricks  that I've found to be most
  212.        helpful in letting  me  understand what's going on without wading
  213.        through a lot of boiler plate.  Chief among these  is  the use of
  214.        single-character tokens, with no embedded spaces,  for  the early
  215.        design work.  I figure that  if  I  can get a parser to recognize
  216.        and deal with I-T-L, I can  get  it  to do the same with IF-THEN-
  217.        ELSE.  And I can.  In the second "lesson,"   I'll  show  you just
  218.        how easy it  is  to  extend  a  simple parser to handle tokens of
  219.        arbitrary length.  As another  trick,  I  completely  ignore file
  220.        I/O, figuring that  if  I  can  read source from the keyboard and
  221.        output object to the screen, I can also do it from/to disk files.
  222.        Experience  has  proven  that  once  a   translator   is  working
  223.        correctly, it's a  straightforward  matter to redirect the I/O to
  224.        files.    The last trick is that I make no attempt  to  do  error
  225.        correction/recovery.   The   programs   we'll  be  building  will
  226.        RECOGNIZE errors, and will not CRASH, but they  will  simply stop
  227.        on the first error ... just like good ol' Turbo does.  There will
  228.        be  other tricks that you'll see as you go. Most of them can't be
  229.        found in any compiler textbook, but they work.
  230.  
  231.        A word about style and efficiency.    As  you will see, I tend to
  232.        write programs in  _VERY_  small, easily understood pieces.  None
  233.        of the procedures we'll  be  working with will be more than about
  234.        15-20 lines long.  I'm a fervent devotee  of  the  KISS  (Keep It
  235.        Simple, Sidney) school of software development.  I  try  to never
  236.        do something tricky or  complex,  when  something simple will do.
  237.        Inefficient?  Perhaps, but you'll like the  results.    As  Brian
  238.        Kernighan has said,  FIRST  make  it  run, THEN make it run fast.
  239.        If, later on,  you want to go back and tighten up the code in one
  240.        of  our products, you'll be able to do so, since the code will be
  241.        quite understandable. If you  do  so, however, I urge you to wait
  242.        until the program is doing everything you want it to.
  243.  
  244.        I  also  have  a  tendency  to  delay  building  a module until I
  245.        discover that I need  it.    Trying  to anticipate every possible
  246.        future contingency can  drive  you  crazy,  and  you'll generally
  247.        guess wrong anyway.    In  this  modern day of screen editors and
  248.        fast compilers, I don't hesitate to change a module when I feel IA6A6
  249.                                      - 4 -A*A*
  250.  
  251. PA A
  252.  
  253.  
  254.  
  255.  
  256.  
  257.        need a more powerful one.  Until then,  I'll  write  only  what I
  258.        need.
  259.  
  260.        One final caveat: One of the principles we'll be sticking to here
  261.        is that we don't  fool  around with P-code or imaginary CPUs, but
  262.        that we will start out on day one  producing  working, executable
  263.        object code, at least in the form of  assembler  language source.
  264.        However, you may not  like  my  choice  of assembler language ...
  265.        it's 68000 code, which is what works on my system (under SK*DOS).
  266.        I  think  you'll  find, though, that the translation to any other
  267.        CPU such as the 80x86 will  be  quite obvious, though, so I don't
  268.        see  a problem here.  In fact, I hope someone out there who knows
  269.        the '86 language better than I do will offer  us  the  equivalent
  270.        object code fragments as we need them.
  271.  
  272.  
  273.        THE CRADLE
  274.  
  275.        Every program needs some boiler  plate  ...  I/O  routines, error
  276.        message routines, etc.   The  programs we develop here will be no
  277.        exceptions.    I've  tried to hold  this  stuff  to  an  absolute
  278.        minimum, however, so that we  can  concentrate  on  the important
  279.        stuff without losing it  among  the  trees.  The code given below
  280.        represents about the minimum that we need to  get  anything done.
  281.        It consists of some I/O routines, an error-handling routine and a
  282.        skeleton, null main program.   I  call  it  our  cradle.    As we
  283.        develop other routines, we'll add them to the cradle, and add the
  284.        calls to them as we  need to.  Make a copy of the cradle and save
  285.        it, because we'll be using it more than once.
  286.  
  287.        There are many different ways to organize the scanning activities
  288.        of  a  parser.   In Unix systems, authors tend to  use  getc  and
  289.        ungetc.  I've had very good luck with the  approach  shown  here,
  290.        which is to use  a  single, global, lookahead character.  Part of
  291.        the initialization procedure  (the  only part, so far!) serves to
  292.        "prime  the  pump"  by reading the first character from the input
  293.        stream.  No other special  techniques are required with Turbo 4.0
  294.        ... each successive call to  GetChar will read the next character
  295.        in the stream.
  296.  
  297.  
  298.        {--------------------------------------------------------------}
  299.        program Cradle;
  300.  
  301.        {--------------------------------------------------------------}
  302.        { Constant Declarations }
  303.  
  304.        const TAB = ^I;
  305.  
  306.        {--------------------------------------------------------------}
  307.        { Variable Declarations }
  308.  
  309.        var Look: char;              { Lookahead Character }A6A6
  310.                                      - 5 -A*A*
  311.  
  312. PA A
  313.  
  314.  
  315.  
  316.  
  317.  
  318.        {--------------------------------------------------------------}
  319.        { Read New Character From Input Stream }
  320.  
  321.        procedure GetChar;
  322.        begin
  323.           Read(Look);
  324.        end;
  325.  
  326.        {--------------------------------------------------------------}
  327.        { Report an Error }
  328.  
  329.        procedure Error(s: string);
  330.        begin
  331.           WriteLn;
  332.           WriteLn(^G, 'Error: ', s, '.');
  333.        end;
  334.  
  335.  
  336.        {--------------------------------------------------------------}
  337.        { Report Error and Halt }
  338.  
  339.        procedure Abort(s: string);
  340.        begin
  341.           Error(s);
  342.           Halt;
  343.        end;
  344.  
  345.  
  346.        {--------------------------------------------------------------}
  347.        { Report What Was Expected }
  348.  
  349.        procedure Expected(s: string);
  350.        begin
  351.           Abort(s + ' Expected');
  352.        end;
  353.  
  354.        {--------------------------------------------------------------}
  355.        { Match a Specific Input Character }
  356.  
  357.        procedure Match(x: char);
  358.        begin
  359.           if Look = x then GetChar
  360.           else Expected('''' + x + '''');
  361.        end;
  362.  
  363.  
  364.        {--------------------------------------------------------------}
  365.        { Recognize an Alpha Character }
  366.  
  367.        function IsAlpha(c: char): boolean;
  368.        begin
  369.           IsAlpha := upcase(c) in ['A'..'Z'];
  370.        end;A6A6
  371.                                      - 6 -A*A*
  372.  
  373. PA A
  374.  
  375.  
  376.  
  377.  
  378.  
  379.        {--------------------------------------------------------------}
  380.        { Recognize a Decimal Digit }
  381.  
  382.        function IsDigit(c: char): boolean;
  383.        begin
  384.           IsDigit := c in ['0'..'9'];
  385.        end;
  386.  
  387.  
  388.        {--------------------------------------------------------------}
  389.        { Get an Identifier }
  390.  
  391.        function GetName: char;
  392.        begin
  393.           if not IsAlpha(Look) then Expected('Name');
  394.           GetName := UpCase(Look);
  395.           GetChar;
  396.        end;
  397.  
  398.  
  399.        {--------------------------------------------------------------}
  400.        { Get a Number }
  401.  
  402.        function GetNum: char;
  403.        begin
  404.           if not IsDigit(Look) then Expected('Integer');
  405.           GetNum := Look;
  406.           GetChar;
  407.        end;
  408.  
  409.  
  410.        {--------------------------------------------------------------}
  411.        { Output a String with Tab }
  412.  
  413.        procedure Emit(s: string);
  414.        begin
  415.           Write(TAB, s);
  416.        end;
  417.  
  418.  
  419.        {--------------------------------------------------------------}
  420.        { Output a String with Tab and CRLF }
  421.  
  422.        procedure EmitLn(s: string);
  423.        begin
  424.           Emit(s);
  425.           WriteLn;
  426.        end;A9A9
  427.  
  428.                                      - 7 -A*A*
  429.  
  430. PA A
  431.  
  432.  
  433.  
  434.  
  435.  
  436.        {--------------------------------------------------------------}
  437.        { Initialize }
  438.  
  439.        procedure Init;
  440.        begin
  441.           GetChar;
  442.        end;
  443.  
  444.  
  445.        {--------------------------------------------------------------}
  446.        { Main Program }
  447.  
  448.        begin
  449.           Init;
  450.        end.
  451.        {--------------------------------------------------------------}
  452.  
  453.  
  454.        That's it for this introduction.  Copy the code above into TP and
  455.        compile it.  Make sure that it compiles and runs  correctly. Then
  456.        proceed to the first lesson, which is on expression parsing.
  457.  
  458.  
  459.        *****************************************************************
  460.        *                                                               *
  461.        *                        COPYRIGHT NOTICE                       *
  462.        *                                                               *
  463.        *   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
  464.        *                                                               *
  465.        *****************************************************************AUAU
  466.  
  467. APAP
  468.  
  469.                                      - 8 -A*A*
  470. @